﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using System.Net;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace Twite
{
    public partial class Twite : Form
    {
        AccessTwitter Twitter;
        DateTime LastTime;
        Setting setting;
        Storage storage;
        ImageList imageList;
        Thread thread;
        IComparer<ListViewItem> ListViewItemSorter;
        Dictionary<Status, List<ListViewItem>> LookupListView;
        Dictionary<ListView, ListView.ListViewItemCollection> ListViewItemCollection;
        Dictionary<ListView, List<ListViewItem>> ListViewItemVirtualCollection;
        Dictionary<ListView, ListView.SelectedIndexCollection> SelectedIndexCollection;
 
        public Twite()
        {
            setting = new Setting();
            if (setting.userId == "")
                setting.ShowDialog();

            storage = new Storage();
            imageList = new ImageList();

            Twitter = new AccessTwitter(setting.userId, setting.password);

            InitializeComponent();
            listView1.StateImageList = imageList;
            listView2.StateImageList = imageList;

            ListViewItemSorter = new ListViewItemComparer();

            LookupListView = new Dictionary<Status, List<ListViewItem>> { };

            ListViewItemCollection = new Dictionary<ListView, ListView.ListViewItemCollection> {
                {listView1,new ListView.ListViewItemCollection(listView1)},
                {listView2,new ListView.ListViewItemCollection(listView2)}
            };

            ListViewItemVirtualCollection = new Dictionary<ListView, List<ListViewItem>> {
                {listView1,new List<ListViewItem>{}},
                {listView2,new List<ListViewItem>{}},
            };

            SelectedIndexCollection = new Dictionary<ListView, ListView.SelectedIndexCollection> {
                {listView1,new ListView.SelectedIndexCollection(listView1)},
                {listView2,new ListView.SelectedIndexCollection(listView2)}
            };
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            thread = new Thread(new ThreadStart(LoadThread));
            thread.Start();
        }

        private void listView_SelectedIndexChanged(object sender, EventArgs e)
        {
            ListView listView = (ListView)sender;
            if (SelectedIndexCollection[listView].Count != 0)
            {
                Status selected = (Status)listView.FocusedItem.Tag;
                label1.Text = selected.createdAt.ToString() + "\n" + selected.ToString();
            }
            IEnumerable<Status> reading = ListViewItemVirtualCollection[listView]
                .Where(x => SelectedIndexCollection[listView].Contains(ListViewItemVirtualCollection[listView].IndexOf(x)))
                .Where(x => (((Status)x.Tag).read == false))
                .Select(x => (Status)x.Tag);
            listView1.BeginUpdate();
            listView2.BeginUpdate();
            foreach (Status selected in reading)
            {
                selected.read = true;

                foreach (ListViewItem item in LookupListView[selected])
                {
                    item.Font = setting.readFont;
                }
            }
            listView1.EndUpdate();
            listView2.EndUpdate();
            
        }

        private void listView_ColumnClick(object sender, ColumnClickEventArgs e)
        {
            if (e.Column == ((ListViewItemComparer)ListViewItemSorter).col)
                ListViewItemSorter = new ListViewItemComparer(e.Column,!((ListViewItemComparer)ListViewItemSorter).flg);
            else
                ListViewItemSorter = new ListViewItemComparer(e.Column);
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            if (!thread.IsAlive)
            {
                thread.Start();
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (textBox1.Text != "")
            {
                new Thread(new ThreadStart(SendUpdate)).Start();
            }
            else
            {
                if (!thread.IsAlive)
                {
                    thread.Start();
                }
            }
        }

        private void SendUpdate()
        {
            string str = "";
            Invoke((MethodInvoker)
                delegate
                {
                    textBox1.Enabled = false;
                    button1.Enabled = false;
                    str = textBox1.Text;
                    if (setting.addStatus)
                        str = textBox1.Text + " [Twite]";
                });
            Twitter.SendUpdate(str);
            Invoke((MethodInvoker)
                delegate
                {
                    textBox1.Text = "";
                    textBox1.Enabled = true;
                    button1.Enabled = true;
                });
            ReloadTimeLine();
        }
        
        protected override bool ProcessDialogKey(Keys keyData)
        {
            if ((textBox1.Focused) && (keyData == Keys.Enter) && (textBox1.Text != ""))
            {
                new Thread(new ThreadStart(SendUpdate)).Start();

                return true;
            }

            return base.ProcessDialogKey(keyData);
        }

        private void 設定ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            setting.ShowDialog();
            Twitter = new AccessTwitter(setting.userId, setting.password);
        }

        private void Twite_FormClosed(object sender, FormClosedEventArgs e)
        {
            thread.Abort();
        }


        //初期起動
        private void LoadThread()
        {
            XElement timeLine = null;
            Invoke((MethodInvoker)delegate { toolStripStatusLabel1.Text = "起動読込[1]"; });
            try
            {
                timeLine = Twitter.GetTimeLine(0);
            }
            catch (WebException ex)
            {
                thread = new Thread(new ThreadStart(LoadThread));
                Invoke((MethodInvoker)
                    delegate
                    {
                        timer1.Enabled = true;
                        toolStripStatusLabel1.Text = ex.Message;
                    });
                return;
            }
            lock (storage)
            {
                foreach (XElement status in timeLine.Elements("status"))
                {
                    storage.AddTimeLine(new Status(status));
                }

                Invoke((MethodInvoker)delegate { timeLineViewAdding(); });
            }
            LastTime = storage.timeLine[storage.timeLine.Count - 3].createdAt;

            Invoke((MethodInvoker)delegate { toolStripStatusLabel1.Text = "起動読込[2]"; });
            try
            {
                timeLine = Twitter.GetTimeLine(1);
            }
            catch (WebException ex)
            {
                thread = new Thread(new ThreadStart(LoadThread));
                Invoke((MethodInvoker)
                    delegate
                    {
                        timer1.Enabled = true;
                        toolStripStatusLabel1.Text = ex.Message;
                    });
                return;
            }

            lock (storage)
            {
                foreach (XElement status in timeLine.Elements("status"))
                {
                    storage.AddTimeLine(new Status(status));
                }
                Invoke((MethodInvoker)delegate { timeLineViewAdding(); });
            }

            Invoke((MethodInvoker)delegate { toolStripStatusLabel1.Text = "起動読込[reply1]"; });
            try
            {
                timeLine = Twitter.GetReplies(0);
            }
            catch (WebException ex)
            {
                thread = new Thread(new ThreadStart(LoadThread));
                Invoke((MethodInvoker)
                    delegate
                    {
                        timer1.Enabled = true;
                        toolStripStatusLabel1.Text = ex.Message;
                    });
                return;
            }

            lock (storage)
            {
                foreach (XElement status in timeLine.Elements("status"))
                {
                    storage.AddTimeLine(new Status(status));
                }
                Invoke((MethodInvoker)delegate { timeLineViewAdding(); });
            }

            Invoke((MethodInvoker)
                delegate
                {
                    timer1.Enabled = true;
                    toolStripStatusLabel1.Text = "読み込み完了";
                });
            thread = new Thread(new ThreadStart(ReloadTimeLine));
        }

        // TL読み込み
        private void ReloadTimeLine()
        {
            Invoke((MethodInvoker)delegate { toolStripStatusLabel1.Text = "TL更新"; });
            XElement timeLine = null;
            try
            {
                timeLine = Twitter.GetTimeLine(0, LastTime);
            }
            catch (WebException ex)
            {
                thread = new Thread(new ThreadStart(ReloadTimeLine));
                Invoke((MethodInvoker)delegate { statusStrip1.Text = ex.Message; });
                return;
            }
            LastTime = storage.timeLine[storage.timeLine.Count - 3].createdAt;
            lock (storage)
            {
                thread = new Thread(new ThreadStart(ReloadTimeLine));
                foreach (XElement status in timeLine.Elements("status"))
                {
                    storage.AddTimeLine(new Status(status));
                }
                Invoke((MethodInvoker)delegate { timeLineViewAdding(); });
            }

            Invoke((MethodInvoker)delegate { toolStripStatusLabel1.Text = "Reply更新"; });
            try
            {
                timeLine = Twitter.GetReplies(0, LastTime);
            }
            catch (WebException ex)
            {
                thread = new Thread(new ThreadStart(ReloadTimeLine));
                Invoke((MethodInvoker)delegate { statusStrip1.Text = ex.Message; });
                return;
            }
            lock (storage)
            {
                foreach (XElement status in timeLine.Elements("status"))
                {
                    storage.AddTimeLine(new Status(status));
                }
                Invoke((MethodInvoker)delegate { timeLineViewAdding(); });
            }
            Invoke((MethodInvoker)delegate { toolStripStatusLabel1.Text = "読み込み完了"; });
            thread = new Thread(new ThreadStart(ReloadTimeLine));
        }

        public void timeLineViewAdding()
        {
            listView1.BeginUpdate();
            listView2.BeginUpdate();
            foreach (Status status in storage.timeLine)
            {
                if(!LookupListView.Keys.Contains<Status>(status))
                {
                    List<ListViewItem> items = new List<ListViewItem> { };
                    ListViewItem item = CreateListViewItem(status);
                    ListViewItemVirtualCollection[listView1].Add(item);
                    items.Add(item);

                    if (status.inReplyToScreenName == setting.userId)
                    {
                        item = CreateListViewItem(status);
                        ListViewItemVirtualCollection[listView2].Add(item);
                        items.Add(item);
                    }
                    LookupListView.Add(status,items);
                }
            }
            ListViewItemVirtualCollection[listView1].Sort(ListViewItemSorter);
            ListViewItemVirtualCollection[listView2].Sort(ListViewItemSorter);
            listView1.VirtualListSize = ListViewItemVirtualCollection[listView1].Count;
            listView2.VirtualListSize = ListViewItemVirtualCollection[listView2].Count;
            listView1.EndUpdate();
            listView2.EndUpdate();
        }

        private ListViewItem CreateListViewItem(Status status)
        {
            ListViewItem Item = new ListViewItem();
            Item.Tag = status;
            Item.Text = "";
            if (status.user.userProtected)
                Item.Text = "!";
            Item.SubItems.Add(
                new ListViewItem.ListViewSubItem(Item, status.user.screenName)
                );
            Item.SubItems.Add(
                new ListViewItem.ListViewSubItem(Item, status.text)
                );
            Item.SubItems.Add(
                new ListViewItem.ListViewSubItem(Item, status.createdAt.ToString("yy-MM-dd HH:mm:ss"))
                );
            
            // プロフィール画像
            Item.ImageKey = status.user.profileImageUrl;
            if (!imageList.Images.ContainsKey(status.user.profileImageUrl))
            {
                imageList.Images.Add(
                    status.user.profileImageUrl,
                    storage.GetImage(status.user.profileImageUrl)
                    .GetThumbnailImage(16, 16, delegate { return false; }, IntPtr.Zero));
            }

            // 未読管理
            if (status.read == true)
            {
                Item.Font = setting.readFont;
            }
            else
            {
                Item.Font = setting.unreadFont;
            }
            return Item;
        }

        #region ListView操作

        private void GoPost(ListView sender, bool next)
        {
            if (SelectedIndexCollection[sender].Count == 0)
                return;
            Status focused = (Status)sender.FocusedItem.Tag;
            IEnumerable<Status> lookup = storage.timeLine
                .ToLookup(x => x.user.screenName)[focused.user.screenName];
            if (next)
            {
                Status stat = focused;
                while(lookup.Count(x => x.createdAt < stat.createdAt) != 0)
                {
                    stat = lookup.First(x => x.createdAt < stat.createdAt);
                    foreach (ListViewItem item in LookupListView[stat])
                        if (sender.Items.Contains(item))
                        {
                            SelectListItem(item);
                            return;
                        }
                }
            }
            else
            {

                Status stat = focused;
                while(lookup.Count(x => x.createdAt > stat.createdAt) != 0)
                {
                    stat = lookup.Last(x => x.createdAt > stat.createdAt);
                    foreach (ListViewItem item in LookupListView[stat])
                        if (sender.Items.Contains(item))
                        {
                            SelectListItem(item);
                            return;
                        }
                }
            }
        }

        private void SelectListItem(ListViewItem item)
        {
            SelectedIndexCollection[item.ListView].Clear();
            SelectedIndexCollection[item.ListView].Add(item.Index);
            item.Focused = true;
        }

        private void JumpUnread()
        {
            IEnumerable<ListViewItem> items = ListViewItemVirtualCollection[listView1].Where(x => ((Status)x.Tag).read == false);
            if (items.Count().Equals(0))
            {
                SelectListItem(ListViewItemVirtualCollection[listView1].Last());
                return;
            }
            SelectListItem(items.First());
        }
        

        private void listView_KeyDown(object sender, KeyEventArgs e)
        {
            if(!e.Alt)
            {
                if (e.KeyCode == Keys.J)
                {
                    e.Handled = true;
                    e.SuppressKeyPress = true;
                    SendKeys.Send("{DOWN}");
                    return;
                }
                if (e.KeyCode == Keys.K)
                {
                    e.Handled = true;
                    e.SuppressKeyPress = true;
                    SendKeys.Send("{UP}");
                    return;
                }
            }
            if (e.Modifiers == Keys.None)
            {
                // ModifierKeyが押されていない場合
                if ((e.KeyCode == Keys.N )||( e.KeyCode == Keys.Right))
                {
                    e.Handled = true;
                    e.SuppressKeyPress = true;
                    //GoRelPost(true);
                    return;
                }
                if ((e.KeyCode == Keys.P)||(e.KeyCode == Keys.Left))
                {
                    e.Handled = true;
                    e.SuppressKeyPress = true;
                    //GoRelPost(False);
                    return;
                }
                if (e.KeyCode == Keys.OemPeriod)
                {
                    e.Handled = true;
                    e.SuppressKeyPress = true;
                    //GoAnchor();
                    return;
                }
                if ((e.KeyCode == Keys.Space)||(e.KeyCode == Keys.ProcessKey))
                {
                    e.Handled = true;
                    e.SuppressKeyPress = true;
                    JumpUnread();
                    return;
                }
                if ((e.KeyCode == Keys.Enter)||(e.KeyCode == Keys.Return))
                {
                    e.Handled = true;
                    e.SuppressKeyPress = true;
                    //MakeReplyOrDirectStatus();
                    return;
                }
                if (e.KeyCode == Keys.L)
                {
                    e.Handled = true;
                    e.SuppressKeyPress = true;
                    GoPost((ListView)sender, false);
                    return;
                }
                if (e.KeyCode == Keys.H)
                {
                    e.Handled = true;
                    e.SuppressKeyPress = true;
                    GoPost((ListView)sender, true);
                    return;
                }
                if ((e.KeyCode == Keys.Z)||( e.KeyCode == Keys.Oemcomma ))
                {
                    e.Handled = true;
                    e.SuppressKeyPress = true;
                    //MoveTop();
                    return;
                }
            }
        }
        #endregion

        private void listView_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
        {
            e.Item = ListViewItemVirtualCollection[(ListView)sender][e.ItemIndex];
        }


    }

    class ListViewItemComparer : IComparer<ListViewItem>
    {
        public int col;
        public bool flg;

        public ListViewItemComparer()
        {
            col = 0;
            flg = true;
        }
        public ListViewItemComparer(int column)
        {
            col = column;
            flg = true;
        }
        public ListViewItemComparer(int column, bool flag)
        {
            col = column;
            flg = flag;
        }

        public int Compare(ListViewItem x, ListViewItem y)
        {
            if (col == 0)
                if (flg)
                    return (((Status)(x.Tag)).CompareTo((Status)(y.Tag)));
                else
                    return (-((Status)(x.Tag)).CompareTo((Status)(y.Tag)));
            else
                if (flg)
                    return (String.Compare(x.SubItems[col].Text, y.SubItems[col].Text));
                else
                    return (-String.Compare(x.SubItems[col].Text, y.SubItems[col].Text));
        }
    }
}
